package com.ctc.lottery.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ctc.lottery.common.BaseContext;
import com.ctc.lottery.entity.*;
import com.ctc.lottery.mapper.LotteryMapper;
import com.ctc.lottery.service.*;
import com.ctc.lottery.utils.RedisLockUtils;
import com.ctc.lottery.vo.LotteryVo;
import com.ctc.lottery.vo.PrizeVo;
import com.ctc.utils.R;
import org.apache.commons.lang.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 抽奖
 * @author 32701
 */
@Service
public class LotteryServiceImpl extends ServiceImpl<LotteryMapper, Lottery> implements LotteryService {
    @Autowired
    private PrizeService prizeService;
    @Autowired
    private IndentService indentService;
    @Autowired
    private MemberService memberService;
    @Autowired
    private CommodityService commodityService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private Redisson redisson;
    /**
     * 设置抽奖活动
     * @param vo
     */
    @Transactional
    @Override
    public void setLottery(LotteryVo vo) {
        // 保存活动
        Lottery lottery = new Lottery();
        lottery.setActivityName(vo.getActivityName());
        lottery.setLotteryNumber(vo.getLotteryNumber());
        this.save(lottery);

        // 保存奖品
        List<PrizeVo> voList = vo.getPrizeVoList();
        List<Prize> collect = voList.stream().map(prizeVo -> {
            Prize prize = new Prize();
            BeanUtils.copyProperties(prizeVo, prize);
            prize.setActivityName(vo.getActivityName());
            prize.setRemaining(prizeVo.getPrizeNumber());
            return prize;
        }).collect(Collectors.toList());

        prizeService.saveBatch(collect);

        // 将用户信息封装进map
        Map map=new HashMap<String,String>();
        for (Member member : memberService.list()) {
            map.put(member.getName(),String.valueOf(vo.getLotteryNumber()));
        }
        String key = "lottery:"+vo.getActivityName();
        // redis保存
        String value = UUID.randomUUID().toString();
        redisTemplate.opsForHash().putAll(key,map);
    }

    /**
     * 抽奖
     * @param activityName
     */
    @Transactional
    @Override
    public R lottery(String activityName) {

        // 活动key值
        String key = "lottery:"+activityName;
        // 该用户目前的抽奖次数
        String numStr = (String) redisTemplate.opsForHash().get(key, BaseContext.getCurrentUserName());
        int num = 0;
        if (numStr!=null){
            num = Integer.parseInt(numStr);
        }
        if (num<=0) {
            return R.error(402,"您的抽奖次数已经用尽");
        }

        // 查询参与抽奖商品
        List<Prize> list = prizeService.list(
                new LambdaQueryWrapper<Prize>().eq(Prize::getActivityName, activityName)
        );
        Prize prize = byWeightGetPrize(list);

        // 封装保存
        Indent indent = new Indent();
        indent.setCommodityName(prize.getCommodityName());
        indent.setCommodityNumber(prize.getCommodityNumber());
        indent.setCommodityPrice(BigDecimal.valueOf(0));
        indent.setMemberName(BaseContext.getCurrentUserName());
        indentService.save(indent);

        // 更新奖品数量和商品数量
        boolean b = upNumber(prize);
        if (b){
            redisTemplate.opsForHash().put(key, BaseContext.getCurrentUserName(),String.valueOf(--num));
        }

        return R.ok().put("data",prize);
    }

    /**
     * 更新奖品数量和商品数量
     * @param prize
     */
    private boolean upNumber(Prize prize) {
        // 获取锁对象
        RLock redissonLock = redisson.getLock(prize.getCommodityId());
        try {
            // 添加锁
            redissonLock.lock(30, TimeUnit.SECONDS);

            // 奖品设置数量校验
            int prizeRemain = prize.getRemaining()-prize.getCommodityNumber();
            if (prizeRemain<0){
                return false;
            }
            prize.setRemaining(prizeRemain);
            // 更新信息
            prizeService.updateById(prize);

            // 查询对应商品
            Commodity commodity = commodityService.getOne(
                    new LambdaQueryWrapper<Commodity>().eq(Commodity::getId, prize.getCommodityId())
            );

            // 商品剩余数量校验
            int commodityAmount = commodity.getAmount()-prize.getCommodityNumber();
            if (commodityAmount<0){
                return false;
            }
            commodity.setAmount(commodityAmount);
            commodityService.updateById(commodity);
        }finally {
            // 释放锁
            redissonLock.unlock();
        }
        return true;
    }

    /**
     * 通过权重获取随机奖品
     * @param list
     * @return
     */
    private Prize byWeightGetPrize(List<Prize> list) {
        // 获取抽奖等级划分和权重
        Map<String, Integer> levelAndWeight = list.stream().collect(Collectors.toMap(Prize::getLevel, Prize::getWeight, Integer::sum));
        // 获取总权重
        int sum = (int) levelAndWeight.values().stream().mapToDouble(Integer::intValue).sum();
        // 准备随机数
        int v = (int) (Math.random() * sum);

        // 遍历查找随机数对应是商品
        Prize prize = new Prize();
        for (int i = 0; i < list.size(); i++) {
            Prize prize1 = list.get(i);
            v-=prize1.getWeight();
            if (v<0){
                prize=prize1;
                break;
            }
        }
        return prize;
    }
}
